// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_HEAP_BARRIER_H_
#define V8_HEAP_BARRIER_H_

#include "src/base/platform/condition-variable.h"
#include "src/base/platform/mutex.h"
#include "src/base/platform/time.h"

namespace v8 {
namespace internal {

    // Barrier that can be used once to synchronize a dynamic number of tasks
    // working concurrently.
    //
    // The barrier takes a timeout which is used to avoid waiting for too long. If
    // any of the users ever reach the timeout they will disable the barrier and
    // signal others to fall through.
    //
    // Usage:
    //   void RunConcurrently(OneShotBarrier* shared_barrier) {
    //     shared_barrier->Start();
    //     do {
    //       {
    //         /* process work and create new work */
    //         barrier->NotifyAll();
    //         /* process work and create new work */
    //       }
    //     } while(!shared_barrier->Wait());
    //   }
    //
    // Note: If Start() is not called in time, e.g., because the first concurrent
    // task is already done processing all work, then Done() will return true
    // immediately.
    class OneshotBarrier {
    public:
        explicit OneshotBarrier(base::TimeDelta timeout)
            : timeout_(timeout)
        {
        }

        void Start()
        {
            base::MutexGuard guard(&mutex_);
            tasks_++;
        }

        void NotifyAll()
        {
            base::MutexGuard guard(&mutex_);
            if (waiting_ > 0)
                condition_.NotifyAll();
        }

        bool Wait()
        {
            base::MutexGuard guard(&mutex_);
            if (done_)
                return true;

            DCHECK_LE(waiting_, tasks_);
            waiting_++;
            if (waiting_ == tasks_) {
                done_ = true;
                condition_.NotifyAll();
            } else {
                // Spurious wakeup is ok here.
                if (!condition_.WaitFor(&mutex_, timeout_)) {
                    // If predefined timeout was reached, Stop waiting and signal being done
                    // also to other tasks.
                    done_ = true;
                }
            }
            waiting_--;
            return done_;
        }

        // Only valid to be called in a sequential setting.
        bool DoneForTesting() const { return done_; }

    private:
        base::ConditionVariable condition_;
        base::Mutex mutex_;
        base::TimeDelta timeout_;
        int tasks_ = 0;
        int waiting_ = 0;
        bool done_ = false;
    };

} // namespace internal
} // namespace v8

#endif // V8_HEAP_BARRIER_H_
